package simpledb.storage;

import simpledb.common.DbException;
import simpledb.transaction.TransactionAbortedException;
import simpledb.transaction.TransactionId;

import java.io.IOException;
import java.util.List;

/**
 * 表示磁盘上数据库文件的接口
 * 每张表由一个 DbFile 实例表示
 * DbFiles 可以读取页面、遍历元组
 * 每个文件有一个唯一的ID，用于在 Catalog 中存储表的元数据
 * DbFiles 通常通过 BufferPool 访问，而不是直接被操作符访问
 * 该接口定义了对数据库文件进行操作的基本方法，包括：
 * 1、读取页面（page）
 * 2、写入页面
 * 3、插入元组（tuple）
 * 4、删除元组
 * 5、遍历所有元组
 * 6、获取唯一标识符（tableId）
 * 7、获取表的模式信息（TupleDesc）
 */
public interface DbFile {

    /**
     * 从磁盘中读取指定的页面。
     *
     * @param id 页面的标识符（包含文件ID和页号）
     * @return 返回读取到的页面对象
     * @throws IllegalArgumentException 如果该页面在此文件中不存在
     */
    Page readPage(PageId id);

    /**
     * 将指定的页面写入磁盘。
     *
     * @param p 要写入的页面对象
     * @throws IOException 如果写入失败
     */
    void writePage(Page p) throws IOException;

    /**
     * 在事务的上下文中将指定的元组插入到文件中。
     * 此方法会对受影响的页面加锁，并可能阻塞直到获取锁。
     *
     * @param tid 执行插入操作的事务
     * @param t   要插入的元组对象
     * @return 包含被修改页面的列表
     * @throws DbException 如果无法添加元组（如空间不足）
     * @throws IOException 如果文件读写失败
     * @throws TransactionAbortedException 如果事务因死锁等原因被中断
     */
    List<Page> insertTuple(TransactionId tid, Tuple t) throws DbException, IOException, TransactionAbortedException;

    /**
     * 在事务的上下文中删除指定的元组。
     * 此方法会对受影响的页面加锁，并可能阻塞直到获取锁。
     *
     * @param tid 执行删除操作的事务
     * @param t   要删除的元组对象
     * @return 包含被修改页面的列表
     * @throws DbException 如果元组不能被删除或不在文件中
     * @throws IOException 如果文件读写失败
     * @throws TransactionAbortedException 如果事务因死锁等原因被中断
     */
    List<Page> deleteTuple(TransactionId tid, Tuple t) throws DbException, IOException, TransactionAbortedException;

    /**
     * 返回一个迭代器，可以遍历此文件中的所有元组。
     * 迭代器必须使用 BufferPool.getPage() 方法来访问页面，而不是直接调用 readPage。
     *
     * @param tid 当前事务ID，用于并发控制
     * @return 一个可以遍历所有元组的迭代器
     */
    DbFileIterator iterator(TransactionId tid);

    /**
     * 返回该文件在 Catalog 中的唯一标识符 tableId。
     * 可用于查找表对应的文件和表结构。
     *
     * <p><b>实现建议：</b>
     * 你需要为每个 HeapFile 生成一个“唯一ID”，并且每次返回相同的值。
     * 一种简单的实现方式是使用底层文件路径的哈希码：
     * <code>f.getAbsoluteFile().hashCode()</code>
     *
     * @return 唯一标识该表的整数ID
     */
    int getId();

    /**
     * 返回该文件中存储的表的元组描述（即表的 schema）。
     *
     * @return 表的元组描述对象（TupleDesc）
     */
    TupleDesc getTupleDesc();
}
